package com.hhkj.payment.wechat.notification.common.parser;

import com.google.gson.Gson;
import com.ruoyi.common.exception.GlobalException;
import com.wechat.pay.java.core.cipher.AeadCipher;
import com.wechat.pay.java.core.cipher.Verifier;
import com.wechat.pay.java.core.notification.Notification;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.core.notification.Resource;
import com.wechat.pay.java.core.util.GsonUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.requireNonNull;

/**
 * WechatNotificationParser
 * 微信通知解析器
 *
 * @author HHKJ-SERVER-ZJ
 * @since 2023/11/22 8:45
 */
@Slf4j
public class Parser {

    private final Gson gson = GsonUtil.getGson();
    private final Map<String, Verifier> verifiers = new HashMap<>();
    private final Map<String, AeadCipher> ciphers = new HashMap<>();


    public Parser(NotificationConfig... configs) {
        if (configs.length == 0) {
            log.error("NotificationConfig is empty.");
            throw new GlobalException("微信配置信息为空");
        }
        for (NotificationConfig config : configs) {
            this.verifiers.put(config.getSignType(), config.createVerifier());
            this.ciphers.put(config.getCipherType(), config.createAeadCipher());
        }
    }

    public <T> T parse(RequestParam requestParam, Class<T> decryptObjectClass) {
        validateRequest(requestParam);
        return getDecryptObject(requestParam, requireNonNull(decryptObjectClass));
    }

    private <T> T getDecryptObject(RequestParam requestParam, Class<T> decryptObjectClass) {
        Notification notification = gson.fromJson(requestParam.getBody(), Notification.class);
        return getDecryptObject(notification, decryptObjectClass);
    }

    public  <T> T getDecryptObject(Notification notification, Class<T> decryptObjectClass) {
        validateNotification(notification);
        String algorithm = notification.getResource().getAlgorithm();
        String associatedData = notification.getResource().getAssociatedData();
        String nonce = notification.getResource().createNonce();
        String ciphertext = notification.getResource().getCiphertext();
        String plaintext = decryptData(algorithm, associatedData, nonce, ciphertext);
        return gson.fromJson(plaintext, decryptObjectClass);
    }

    /**
     * 校验解析参数信息
     *
     * @param notification 通知信息校验
     */
    private void validateNotification(Notification notification) {
        if (notification == null) {
            log.error("The notification obtained by parsing the WechatPay notification is null.");
            throw new GlobalException("请求参数解析异常");
        }
        Resource resource = notification.getResource();
        if (resource == null) {
            log.error(String.format(
                    "The resource obtained by parsing the WechatPay notification is null"
                            + ".Notification[%s]",
                    notification));
            throw new GlobalException("请求参数解析异常");
        }
        if (resource.getAlgorithm() == null) {
            log.error(String.format(
                    "The algorithm obtained by parsing the WechatPay notification is empty.Notification[%s]",
                    notification));
            throw new GlobalException("请求参数解析异常");
        }
        if (resource.getCiphertext() == null) {
            log.error(String.format(
                    "The ciphertext obtained by parsing the WechatPay notification is empty.Notification[%s]",
                    notification));
            throw new GlobalException("请求参数解析异常");
        }
        if (resource.createNonce() == null) {
            log.error(String.format(
                    "The nonce obtained by parsing the WechatPay notification is empty.Notification[%s]",
                    notification));
            throw new GlobalException("请求参数解析异常");
        }
    }

    private String decryptData(
            String algorithm, String associatedData, String nonce, String ciphertext) {
        AeadCipher aeadCipher = ciphers.get(algorithm);
        if (aeadCipher == null) {
            log.error("Parse WechatPay notification,There is no AeadCipher corresponding to the algorithm.");
            throw new GlobalException("请求参数解析异常");
        }
        return aeadCipher.decrypt(
                associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                Base64.getDecoder().decode(ciphertext));
    }

    /**
     * 解析微信支付回调通知
     * Params:
     * requestParam – 解析通知所需要的请求参数
     * Returns:
     * 解密后的回调报文
     * Throws:
     * MalformedMessageException – 回调通知参数不正确、解析通知数据失败
     * ValidationException – 签名验证失败
     */
    public Notification getNotificationFromRequestParam(RequestParam requestParam) {
        this.validateRequest(requestParam);
        return gson.fromJson(requestParam.getBody(), Notification.class);
    }

    /**
     * 解析请求信息
     *
     * @param requestParam 请求参数
     */
    private void validateRequest(RequestParam requestParam) {
        if (requestParam == null) {
            log.error("Verify WechatPay notification parameters, requestParam is null.");
            throw new GlobalException("请求参数解析异常");
        }
        if (requestParam.getSignType() == null) {
            log.error(String.format(
                    "Verify WechatPay notification parameters, signType is empty" + ".RequestParam[%s]",
                    requestParam));
            throw new GlobalException("请求参数解析异常");
        }
        if (requestParam.getSerialNumber() == null) {
            log.error(String.format(
                    "Verify WechatPay notification parameters, serialNumber is empty"
                            + ".RequestParam[%s]",
                    requestParam));
            throw new GlobalException("请求参数解析异常");
        }
        if (requestParam.getMessage() == null) {
            log.error(String.format(
                    "Verify WechatPay notification parameters, message is empty" + ".RequestParam[%s]",
                    requestParam));
            throw new GlobalException("请求参数解析异常");
        }
        if (requestParam.getSignature() == null) {
            log.error(String.format(
                    "Verify WechatPay notification parameters, signature is empty" + ".RequestParam[%s]",
                    requestParam));
            throw new GlobalException("请求参数解析异常");
        }
        Verifier verifier = verifiers.get(requestParam.getSignType());
        if (verifier == null) {
            log.error(String.format(
                    "Processing WechatPay notification, there is no verifier corresponding to signType[%s]",
                    requestParam.getSignType()));
            throw new GlobalException("请求参数解析异常");
        }
        if (!verifier.verify(
                requestParam.getSerialNumber(), requestParam.getMessage(), requestParam.getSignature())) {
            log.error(String.format(
                    "Processing WechatPay notification,signature verification failed,"
                            + "signType[%s]\tserial[%s]\tmessage[%s]\tsign[%s]",
                    requestParam.getSignType(),
                    requestParam.getSerialNumber(),
                    requestParam.getMessage(),
                    requestParam.getSignature()));
            throw new GlobalException("请求参数解析异常");
        }
    }

}
